package com.mdc.mansion.common.util.builder;

import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;

/**
 * @author xuyang
 * 
 */
public class CustomToStringBuilder {

	public static final CustomToStringStyle customStyle = new CustomToStringStyle();

	/**
	 * Current toString buffer, not null.
	 */
	private final StringBuffer buffer;

	/**
	 * The object being output, may be null.
	 */
	private final Object object;

	/**
	 * The style of output to use, not null.
	 */
	private final CustomToStringStyle style;

	/**
	 * Whether or not to append static fields.
	 */
	private boolean appendStatics = false;

	/**
	 * Whether or not to append transient fields.
	 */
	private boolean appendTransients = false;

	/**
	 * Which field names to exclude from output. Intended for fields like
	 * <code>"password"</code>.
	 */
	private String[] excludeFieldNames;

	/**
	 * The last super class to stop appending fields for.
	 */
	private Class<?> upToClass = null;

	private ReflectionToStringBuilder builder = null;

	public CustomToStringBuilder(Object object) {
		this(object, null);
	}

	public CustomToStringBuilder(Object object, CustomToStringStyle style, StringBuffer buffer,
			Class<?> reflectUpToClass, boolean outputTransients, boolean outputStatics) {
		this(object, style, buffer);
		this.setUpToClass(reflectUpToClass);
		this.setAppendTransients(outputTransients);
		this.setAppendStatics(outputStatics);
	}

	public CustomToStringBuilder(Object object, CustomToStringStyle style, StringBuffer buffer) {
		if (style == null) {
			style = customStyle;
		}
		if (buffer == null) {
			buffer = new StringBuffer(512);
		}
		this.buffer = buffer;
		this.style = style;
		this.object = object;
		this.style.setCustomToStringBuilder(this);
	}

	public CustomToStringBuilder(Object object, CustomToStringStyle style) {
		this(object, style, null);
	}

	public String toString() {
		Object obj = this.getObject();
		if (obj == null) {
			return customStyle.getNullText();
		} else if (obj instanceof Number || obj instanceof Boolean || obj instanceof Character
				|| obj instanceof CharSequence) {
			this.reflectionAppend(obj);
		} else if (obj instanceof Date) {
			this.reflectionAppend((Date) obj);
		} else if (obj.getClass().isArray()) {
			this.reflectionAppendArray(obj);
		} else if (obj instanceof Collection<?>) {
			this.reflectionAppend((Collection<?>) obj);
		} else if (obj instanceof Map<?, ?>) {
			this.reflectionAppend((Map<?, ?>) obj);
		} else if (obj instanceof Enumeration<?>) {
			this.reflectionAppend((Enumeration<?>) obj);
		} else if (obj instanceof Enum<?>) {
			this.reflectionAppend((Enum<?>) obj);
		} else {
			builder = new ReflectionToStringBuilder(obj, style, buffer, upToClass, appendTransients, appendStatics);
			builder.setExcludeFieldNames(excludeFieldNames);
			return builder.toString();
		}
		return buffer.toString();
	}

	protected void reflectionAppend(Object obj) {
		style.appendDetail(buffer, null, obj);
	}

	protected void reflectionAppend(Date date) {
		style.appendDetail(buffer, null, date);
	}

	protected void reflectionAppendArray(Object array) {
		style.reflectionAppendArrayDetail(buffer, null, array);
	}

	protected void reflectionAppend(Collection<?> coll) {
		style.appendDetail(buffer, null, coll);
	}

	protected void reflectionAppend(Map<?, ?> map) {
		style.appendDetail(buffer, null, map);
	}

	protected void reflectionAppend(Enumeration<?> enumeration) {
		style.appendDetail(buffer, null, enumeration);
	}

	protected void reflectionAppend(Enum<?> e) {
		style.appendDetail(buffer, null, e);
	}

	/**
	 * @return the buffer
	 */
	public StringBuffer getStringBuffer() {
		return buffer;
	}

	/**
	 * @return the object
	 */
	public Object getObject() {
		return object;
	}

	/**
	 * @return the style
	 */
	public CustomToStringStyle getStyle() {
		return style;
	}

	/**
	 * @return the appendStatics
	 */
	public boolean isAppendStatics() {
		return appendStatics;
	}

	/**
	 * @param appendStatics
	 *            the appendStatics to set
	 */
	public void setAppendStatics(boolean appendStatics) {
		this.appendStatics = appendStatics;
	}

	/**
	 * @return the appendTransients
	 */
	public boolean isAppendTransients() {
		return appendTransients;
	}

	/**
	 * @param appendTransients
	 *            the appendTransients to set
	 */
	public void setAppendTransients(boolean appendTransients) {
		this.appendTransients = appendTransients;
	}

	/**
	 * @return the excludeFieldNames
	 */
	public String[] getExcludeFieldNames() {
		return excludeFieldNames;
	}

	/**
	 * @param excludeFieldNames
	 *            the excludeFieldNames to set
	 */
	public void setExcludeFieldNames(String[] excludeFieldNames) {
		this.excludeFieldNames = excludeFieldNames;
	}

	/**
	 * @return the upToClass
	 */
	public Class<?> getUpToClass() {
		return upToClass;
	}

	/**
	 * @param upToClass
	 *            the upToClass to set
	 */
	public void setUpToClass(Class<?> upToClass) {
		if (upToClass == null) {
			upToClass = Object.class;
		}
		this.upToClass = upToClass;
	}

}
